package com.innjia.app.controller;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSONObject;
import com.innjia.sys.service.impl.QuKuaiUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.innjia.base.common.ResultInfo;
import com.innjia.base.common.ZimaConfig.SignPosKeywordEnum;
import com.innjia.base.enums.ContractStatusEnum;
import com.innjia.base.enums.HouseStatusEnum;
import com.innjia.base.utils.Base64Util;
import com.innjia.base.utils.ContractTemplateUtil;
import com.innjia.base.utils.DateConverUtil;
import com.innjia.base.utils.DateUtils;
import com.innjia.base.utils.UrlParamMapUtil;
import com.innjia.base.utils.Utils;
import com.innjia.base.utils.pdf.PdfUtils;
import com.innjia.base.utils.pdf.WaterMarks;
import com.innjia.base.zima.sdk.ZhimaContractSdk;
import com.innjia.sys.entity.ContractInfoEntity;
import com.innjia.sys.entity.ContractTemplateEntity;
import com.innjia.sys.entity.HouseEntity;
import com.innjia.sys.entity.SignRequestInfoEntity;
import com.innjia.sys.entity.UserEntity;
import com.innjia.sys.entity.UserLandlordEntity;
import com.innjia.sys.entity.UserTenantEntity;
import com.innjia.sys.service.ContractInfoService;
import com.innjia.sys.service.ContractTemplateService;
import com.innjia.sys.service.FileService;
import com.innjia.sys.service.HouseService;
import com.innjia.sys.service.SignRequestInfoService;
import com.innjia.sys.service.UserLandlordService;
import com.innjia.sys.service.UserTenantService;
import com.innjia.utils.ShiroUtils;

/**
 * 
 * @author fangcheng.huang
 *
 */
@Controller
@RequestMapping("/app/contract")
public class ContractSignController extends BaseController {

	private static Logger logger = Logger.getLogger(ContractSignController.class);
	
	@Autowired
	private SignRequestInfoService signRequestInfoService;
	
	@Autowired
	private ContractInfoService contractInfoService;
	
	@Autowired
	private HouseService houseService;
	
	@Autowired
	private UserTenantService userTenantService;
	
	@Autowired
	private UserLandlordService userLandlordService;
	
	@Autowired
	private FileService fileService;
	
	@Autowired
	private ContractTemplateService contractTemplateService;

	@Autowired
	private QuKuaiUtil quKuaiUtil;
	
	/*
	 * 合同生成
	 */
	@RequestMapping(value = "/create", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	@ResponseBody
	public ResultInfo createContract(HttpServletRequest request, HttpServletResponse response,
			@ModelAttribute ContractInfoEntity model) {
		ResultInfo res = new ResultInfo("1", "创建合同失败");
		UserEntity userEntity = ShiroUtils.getUserEntity();
		if(userEntity == null) {
			res.setMsg("用户未登录");
			return res;
		}
		String loginUserId = userEntity.getId();
		String houseId = model.getHouseId();
		HouseEntity house = houseService.queryObject(houseId);
		if(house == null) {
			res.setMsg("房源不存在");
			return res;
		}
		String landlordId = house.getOwnerId();
		model.setLandlordId(landlordId);
		UserLandlordEntity landlordInfo = (UserLandlordEntity) userLandlordService.queryObject(landlordId);
		if(landlordInfo == null) {
			res.setMsg("该房源房东信息不存在");
			return res;
		}
		if(!HouseStatusEnum.FREE.getDesc().equals(house.getRentStatus()) || !"审核通过".equals(house.getApplyStatus())) {
			res.setMsg("房源已出租，或审核未通过");
			return res;
		}
		if(model.getTenantIdCard() == null || model.getTenantName() == null || model.getTenantPhone() == null) {
			res.setMsg("租客信息不完整");
			return res;
		}
		UserEntity tenantInfo = userTenantService.queryByPhone(model.getTenantPhone());
		if(tenantInfo != null) {
			model.setTenantId(tenantInfo.getId());
		}
		if(model.getContractValidityStartTime() == null || model.getTotalMonth() == null) {
			res.setMsg("租约时间信息不完整");
			return res;
		}
		if(model.getDeposit() == null || model.getAdvanceDays() == null) {
			res.setMsg("押金及提前交租天数不能为空");
			return res;
		}
		model.setHouseCode(house.getSerialNum());
		model.setContractStatus(ContractStatusEnum.WAIT_SIGN.getCode());
		model.setContractValidityEndTime(DateUtils.contractValidityEndTime(model.getContractValidityStartTime(), model.getTotalMonth()));
		if(model.getMonthRent() == null) {
			model.setMonthRent(new BigDecimal(house.getRentalPrice()));
		}
		model.setTotalRent(model.getMonthRent().multiply(new BigDecimal(model.getTotalMonth())));
		model.setHouseCode(house.getSerialNum());
		if(StringUtils.isEmpty(model.getPayWay())) {
			model.setPayWay(house.getPayMode());
		}
		model.setLeaseWay(house.getRentType());
		model.setLeaseAddress(house.hosueDetailAddress());
		String idCard = landlordInfo.getIdCard();
		String userName = landlordInfo.getUserName();
		String landlordPhone = landlordInfo.getPhone();
		if(StringUtils.isEmpty(idCard) || StringUtils.isEmpty(userName) || StringUtils.isEmpty(landlordPhone)) {
			res.setMsg("房东信息不完善,请完善房东信息");
			return res;
		}
		model.setLandlordIdCard(idCard);
		model.setLandlordName(userName);
		model.setLandlordPhone(landlordPhone);
		model.setContractCreatorId(loginUserId);
		Date date = new Date();
		model.setCreateTime(date);
		model.setUpdateTime(date);
		//生成合同文件
		ContractTemplateEntity contractTemplate = contractTemplateService.queryByContractType("XAZN");
		if(contractTemplate == null) {
			res.setMsg("合同模板信息不存在，无法生存合同");
			return res;
		}
		//根据模板生成合同
		FileInputStream inputStream = null;
		String fileEntityId = null;
		File file = null;
		try {
			String template = contractTemplate.getTemplate();
			Map<String, Object> templateParams = new HashMap<String, Object>(128);
			house.getMapDatas(templateParams);
			String contractContent = ContractTemplateUtil.contractGenerationResult(model.getMapDatas(templateParams), template);
			String path = PdfUtils.generatePdf(contractTemplate.getContractName(), null, contractContent);
			file = new File(path);
			inputStream = new FileInputStream(file);
			byte[] b = new byte[inputStream.available()];
			inputStream.read(b, 0, inputStream.available());
			fileEntityId = fileService.saveFileWithType(null, b, File.separator + "contract" + File.separator + "pdf" + File.separator + "original", "pdf");
		} catch (Exception e) {
			logger.error("合同PDF文件保存异常", e);
		} finally {
			if(file != null && file.exists()) {
				file.delete();
			}
			if(inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					logger.error("流关闭异常", e);
				}
			}
		}
		if(StringUtils.isEmpty(fileEntityId)) {
			res.setMsg("发送租客生成合同文件失败");
			return res;
		}
		model.setContractFile(fileEntityId);
		model.setSignDeadlineTime(DateConverUtil.timeCalculate(date, Calendar.HOUR_OF_DAY, 72));
		//生成合同并初始化合约
		if(contractInfoService.saveContract(model)) {
			res.setCode("0");
			res.setMsg("合同保存成功");
			res.setData(model.getId());
		}
		return res;
	}
	
	/**
	 * 返回签名URL，app端跳转进行核身
	 * @param request
	 * @param response
	 * @return
	 * String
	 */
	@RequestMapping(value = "/sign", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	@ResponseBody
	public ResultInfo signContract(HttpServletRequest request, HttpServletResponse response,
			String contractId) {
		ResultInfo result = new ResultInfo();
		String signKeyword = null;
		UserEntity userEntity = ShiroUtils.getUserEntity();
		if(userEntity instanceof UserLandlordEntity) {
			signKeyword = SignPosKeywordEnum.PARTY_A.name();
		} else if(userEntity instanceof UserTenantEntity) {
			signKeyword = SignPosKeywordEnum.PARTY_B.name();
		}
		if(StringUtils.isEmpty(signKeyword)) {
			result.setCode("1");
			result.setMsg("用户身份错误");
			return result;
		}
		ContractInfoEntity contractInfo = contractInfoService.queryObject(contractId);
		if(contractInfo == null || !"0".equals(contractInfo.getContractStatus())) {
			result.setCode("1");
			result.setMsg("合同状态错误，不允许签约");
			return result;
		}
		Date date = new Date();
		if(contractInfo.getSignDeadlineTime().compareTo(new Date()) < 0) {
			contractInfo.setContractStatus(ContractStatusEnum.TERMINATING.getCode());
			contractInfo.setUpdateTime(new Date());
			contractInfoService.update(contractInfo);
			result.setCode("1");
			result.setMsg("签约失效已过，合同作废");
			return result;
		}
		HouseEntity houseEntity = houseService.queryObject(contractInfo.getHouseId());
		if(houseEntity == null) {
			result.setCode("1");
			result.setMsg("合同房源不存在");
			return result;
		}
		if(!HouseStatusEnum.FREE.getDesc().equals(houseEntity.getRentStatus()) || !"审核通过".equals(houseEntity.getApplyStatus())) {
			contractInfo.setContractStatus(ContractStatusEnum.TERMINATING.getCode());
			contractInfo.setUpdateTime(new Date());
			contractInfoService.update(contractInfo);
			result.setCode("1");
			result.setMsg("房屋已出租");
			return result;
		}
		String certName = null;
		String certNo = null;
		String signPhone = null;
		if(SignPosKeywordEnum.PARTY_A.name().equals(signKeyword)) { //甲方或者房东
			certName = contractInfo.getLandlordName();
			certNo = contractInfo.getLandlordIdCard();
			signPhone = contractInfo.getLandlordPhone();
		} else {
			if(!"0".equals(contractInfo.getSuccessA())) {
				result.setCode("1");
				result.setMsg("甲方未签约");
				return result;
			};
			certName = contractInfo.getTenantName();
			certNo = contractInfo.getTenantIdCard();
			signPhone = contractInfo.getTenantPhone();
		}
		if(StringUtils.isEmpty(certName) || StringUtils.isEmpty(certNo) || StringUtils.isEmpty(signPhone)) {
			result.setCode("1");
			result.setMsg("参数错误，有必填信息未填");
			return result;
		}
		SignRequestInfoEntity entity = new SignRequestInfoEntity();
		entity.setContractId(contractId);
		entity.setCreateTime(date);
		entity.setIdCard(certNo);
		entity.setUpdateTime(date);
		entity.setUserPhone(signPhone);
		entity.setOrderId(SignRequestInfoEntity.generaOrderId());
		entity.setUserName(certName);
		entity.setSignResult(1);//默认失败
		entity.setSignKeyword(signKeyword);
		if(signRequestInfoService.save(entity) > 0) {
			result = contractInfoService.updateContractSign(contractId, certName, certNo, signKeyword, entity);
		} else {
			logger.info("记录签约请求信息失败");
			result.setCode("1");
			result.setMsg("请求签约失败");
			return result;
		}
		if(result != null && "0".equals(result.getCode())) {
			entity.setSignResult(0);
		} else {
			entity.setFailReason(result == null ? null : result.getMsg());
		}
		signRequestInfoService.update(entity);
		return result;
	}
	
	/**
	 * 查看pdf合同
	 * @param contractId
	 * @return
	 * ResultInfo
	 */
	@RequestMapping(value="/query/pdf", method = {RequestMethod.GET, RequestMethod.POST})
	@ResponseBody
	public void queryContractPdf(HttpServletRequest request, HttpServletResponse response, 
			String contractId) {
		ContractInfoEntity contractInfo = contractInfoService.queryObject(contractId);
		if(contractInfo == null) {
			logger.info("=======合同不存在，contractInfoId:" + contractId);
			return;
		}
		String bizNo = null;
		byte[] fileBytes = null;
		String contractFileId = null;
		if("0".equals(contractInfo.getSuccessB())) {
			contractFileId = contractInfo.getPartyBContract();
			if(StringUtils.isEmpty(contractFileId)) {
				bizNo = contractInfo.getBizNoB();
				fileBytes = this.getContractByte(bizNo);
				//添加区块链存证章印
				byte[] b = WaterMarks.imageWaterMarks(fileBytes);
				contractFileId = fileService.saveFileWithType(null, b, File.separator + "contract" + File.separator + "pdf" + File.separator + "partyB", "pdf");
				contractInfo.setPartyBContract(contractFileId);
				contractInfoService.update(contractInfo);
			}
		}
		if(StringUtils.isEmpty(contractFileId) && "0".equals(contractInfo.getSuccessA())) {
			contractFileId = contractInfo.getPartyAContract();
			if(StringUtils.isEmpty(contractFileId)) {
				bizNo = contractInfo.getBizNoA();
				fileBytes = this.getContractByte(bizNo);
				contractFileId = fileService.saveFileWithType(null, fileBytes, File.separator + "contract" + File.separator + "pdf" + File.separator + "partyA", "pdf");
				contractInfo.setPartyAContract(contractFileId);
				contractInfoService.update(contractInfo);
			}
		}
		OutputStream out = null;
		try {
			response.reset();
			response.setContentType("application/force-download");// 设置强制下载不打开
			String fileName = new String("房屋租赁合同".getBytes(), "ISO-8859-1");
			response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".pdf");
			response.setContentType("application/pdf;charset=UTF-8");
			if(StringUtils.isEmpty(contractFileId)) {
				contractFileId = contractInfo.getContractFile();
			}
			fileBytes = fileService.getFile(contractFileId);
			if(fileBytes == null || fileBytes.length <= 0) {
				logger.info("============合同文件不存在");
				return;
			}
			response.addHeader("Content-Length", String.valueOf(fileBytes == null ? 0 : fileBytes.length));
			out = new BufferedOutputStream(response.getOutputStream());
			out.write(fileBytes);
			out.flush();
		} catch (Exception e) {
			logger.error("签约认证结果查询异常，", e);
		} finally {
			try {
				if(out != null) {
					out.close();
				}
			} catch (IOException e) {
				logger.error("流关闭异常", e);
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 核身签约回调接口，返回签约结果
	 * @param request
	 * @param response
	 * @param params
	 * @param sign
	 * @return
	 * String
	 */
	@RequestMapping(value = "/sign/callback", method = {RequestMethod.GET, RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	@ResponseBody
	public ResultInfo signContractCallBack(HttpServletRequest request, HttpServletResponse response,
			String params, String sign) {
		ResultInfo result = new ResultInfo("0");
		ZhimaContractSdk sdk = new ZhimaContractSdk();
		//passed=false&biz_no=ZM201711193000000595900523054942
		String checkSignAndDecrypt = sdk.queryCertificationResult(params, sign);
		logger.info("======签约回调解析结果：" + checkSignAndDecrypt);
		if(!StringUtils.isEmpty(checkSignAndDecrypt)) {
			logger.info("=======签约结果：checkSignAndDecrypt=" + checkSignAndDecrypt);
			Map<String, String> mapParam = UrlParamMapUtil.urlParamToMap(checkSignAndDecrypt);
			String passed = StringUtils.isEmpty(mapParam.get("passed")) ? null : mapParam.get("passed").toLowerCase();
			boolean flag = "true".equals(passed);
			String bizNo = mapParam.get("biz_no");
			ContractInfoEntity contractInfo = null;
			contractInfo = contractInfoService.queryByBizNoB(bizNo);
			if(contractInfo != null) {
				synchronized (ContractSignController.class) {
					if("0".equals(contractInfo.getSuccessB())) {//防止前端刷新页面导致一次回调多次请求
						return result;
					}
					contractInfo.setUpdateTime(new Date());
					HouseEntity house = houseService.queryObject(contractInfo.getHouseId());
					if(house == null || HouseStatusEnum.LEASED.getDesc().equals(house.getRentStatus())) {
						flag = false;
						result.setMsg("房屋已出租，合同失效");
						contractInfo.setContractStatus(ContractStatusEnum.TERMINATING.getCode());
					}
					if(flag) {//双方签约成功
						contractInfo.setContractStatus(ContractStatusEnum.SIGNED.getCode());
						contractInfo.setNetSignCode(Utils.uuid());
						contractInfo.setSuccessB("0");
						contractInfo.setSignTimeB(new Date());
						result.setMsg("签约完成，合同生效");
						contractInfoService.updateContractAndHouseStatus(contractInfo, HouseStatusEnum.LEASED.getDesc());
					} else {
						contractInfo.setSuccessB("1");
						contractInfoService.update(contractInfo);
						if(StringUtils.isEmpty(result.getMsg())) {
							result.setMsg("签约失败，请重新签约");
						}
					}
				}
				
				//TODO:
				new Thread(() -> {
					ContractInfoEntity contractI =  contractInfoService.queryByBizNoB(bizNo);
					logger.info("======区块链调用：" );
					String s = JSONObject.toJSONString(contractI);
					try {
						String txHash = quKuaiUtil.saveTransactionWrite(s);
						contractI.setTxHash(txHash);
						contractInfoService.update(contractI);
						
					} catch (Exception e) {
						logger.info("======区块链调用错误");
						e.printStackTrace();
						
					}
					logger.info("======区块链结束：" );
				}).start();
				
			} else {
				contractInfo = contractInfoService.queryByBizNoA(bizNo);
				contractInfo.setUpdateTime(new Date());
				if(flag) {
					contractInfo.setSuccessA("0");
					contractInfo.setSignTimeA(new Date());
					//更新签约期限
					contractInfo.setSignDeadlineTime(DateConverUtil.timeCalculate(new Date(), Calendar.HOUR_OF_DAY, 72));
					result.setMsg("签约成功，请等待乙方签约");
				} else {
					contractInfo.setSuccessA("1");
					result.setMsg("签约失败，请重新签约");
				}
				contractInfoService.update(contractInfo);
			}
		} else {
			logger.info("==============未获取到签约结果");
			result.setMsg("未获取到签约结果");
		}
		return result;
	}
	
	/**
	 * 拒绝合同
	 * @param contractInfoId
	 * @return
	 * ResultInfo
	 */
	@RequestMapping(value = "/sign/reject", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	@ResponseBody
	public ResultInfo rejectSign(String contractInfoId) {
		ResultInfo res = new ResultInfo("1", "操作失败");
		ContractInfoEntity contractInfo = contractInfoService.queryObject(contractInfoId);
		if(contractInfo == null) {
			res.setMsg("合同信息不存在");
			return res;
		}
		contractInfo.setUpdateTime(new Date());
		contractInfo.setContractStatus(ContractStatusEnum.TERMINATING.getCode());
		if(contractInfoService.update(contractInfo) > 0) {
			res.setCode("0");
			res.setMsg("操作成功");
		}
		return res;
	}
	
	private byte[] getContractByte(String bizNo) {
		ResultInfo res = contractInfoService.queryCertificationResult(bizNo);
		byte[] fileBytes = null;
		if("0".equals(res.getCode())) {
			String base64Contract = (String)res.getData();
			fileBytes = Base64Util.decode(base64Contract);
		}
		return fileBytes;
	}
}
